﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text.RegularExpressions;

namespace Scrabble.Model
{
    public sealed class PlayVector
    {
        public PlayVector() : this(BoardLocation.Zero, true)
        {
        }

        public PlayVector(PlayVector vector) : this(vector.StartingLocation, vector.IsAcross)
        {
        }

        public PlayVector(BoardLocation startingLocation, bool isAcross)
        {
            Contract.Requires(startingLocation != null);

            StartingLocation = startingLocation;
            IsAcross = isAcross;
            Length = int.MaxValue;
        }

        public BoardLocation StartingLocation
        {
            get;
            set;
        }

        public int Length
        {
            get;
            set;
        }

        public int ColumnsSpanned
        {
            get { return IsAcross ? Math.Min(Length, Board.Columns - StartingLocation.Column) : 1; }
        }

        public int RowsSpanned
        {
            get { return IsAcross ? 1 : Math.Min(Length, Board.Rows - StartingLocation.Row); }
        }

        public bool IsAcross
        {
            get;
            set;
        }

        public PlayDirection Direction
        {
            get { return IsAcross ? PlayDirection.Across : PlayDirection.Down; }
            set { IsAcross = value == PlayDirection.Across; }
        }

        public PlayDirection PerpendicularDirection
        {
            get { return IsAcross ? PlayDirection.Down : PlayDirection.Across; }
        }

        [Pure]
        public IEnumerable<BoardLocation> Locations
        {
            get
            {
                return Enumerable.Repeat(StartingLocation, 1).Concat(IsAcross ? StartingLocation.FollowingLocationsAcross : StartingLocation.FollowingLocationsDown);
            }
        }

        public void Skip(int skip)
        {
            if (IsAcross)
                StartingLocation.Column += skip;
            else
                StartingLocation.Row += skip;
        }

        public override string ToString()
        {
            var row = (char) ('A' + StartingLocation.Column);
            var col = StartingLocation.Row + 1;

            return String.Format(IsAcross ? "{0}{1}" : "{1}{0}", col, row);
        }

        public static PlayVector Parse(string notation)
        {
            Contract.Requires(notation != null);

            try
            {
                var searchExpression = string.Format("((?<isDown>{0}{1})|({1}{0}))", @"(?<column>[A-O])", @"(?<row>(\d{1,2}))");
                var searchResult = Regex.Match(notation, searchExpression).Groups;

                var location = new BoardLocation(searchResult["column"].Value[0] - 'A', Int32.Parse(searchResult["row"].Value) - 1);
                if (!location.IsWithinBounds) throw new FormatException();

                return new PlayVector
                {
                    Direction = searchResult["isDown"].Success ? PlayDirection.Down : PlayDirection.Across,
                    StartingLocation = location
                };
            }
            catch (IndexOutOfRangeException)
            {
            }
            catch (FormatException)
            {
            }

            return null;
        }

        public static PlayVector AcrossFrom(BoardLocation startingLocation, int length = Board.Columns)
        {
            return new PlayVector(startingLocation, true);
        }

        public static PlayVector DownFrom(BoardLocation startingLocation, int length = Board.Rows)
        {
            return new PlayVector(startingLocation, false);
        }
    }
}